/**********************************************************************

Audacity: A Digital Audio Editor

ProjectAudioManager.h

Paul Licameli split from ProjectManager.h

**********************************************************************/

#ifndef __AUDACITY_PROJECT_AUDIO_MANAGER__
#define __AUDACITY_PROJECT_AUDIO_MANAGER__

#include <memory>
#include <vector>

#include "AudioIOListener.h" // to inherit
#include "ClientData.h" // to inherit
#include "Observer.h"
#include "Observer.h"

#include <atomic>

constexpr int RATE_NOT_SELECTED{ -1 };

class AudacityProject;
struct AudioIOStartStreamOptions;
class TrackList;
class SelectedRegion;
class WritableSampleTrack;
using WritableSampleTrackArray
    =std::vector< std::shared_ptr< WritableSampleTrack > >;

enum class PlayMode : int {
    normalPlay,
    oneSecondPlay, // Disables auto-scrolling
    loopedPlay, // Possibly looped play (not always); disables auto-scrolling
    cutPreviewPlay
};

struct TransportSequences;

using StatusBarField = Identifier;
enum class ProjectFileIOMessage : int;

//! Notification, after recording has stopped, when dropouts have been detected
struct RecordingDropoutEvent {
    //! Start time and duration
    using Interval = std::pair<double, double>;
    using Intervals = std::vector<Interval>;

    explicit RecordingDropoutEvent(const Intervals& intervals)
        : intervals{intervals}
    {}

    //! Disjoint and sorted increasingly
    const Intervals& intervals;
};

class AUDACITY_DLL_API ProjectAudioManager final : public ClientData::Base, public AudioIOListener,
    public std::enable_shared_from_this< ProjectAudioManager >, public Observer::Publisher<RecordingDropoutEvent>
{
public:
    static ProjectAudioManager& Get(AudacityProject& project);
    static const ProjectAudioManager& Get(const AudacityProject& project);

    //! Find suitable tracks to record into, or return an empty array.
    static WritableSampleTrackArray ChooseExistingRecordingTracks(
        AudacityProject& proj, bool selectedOnly, double targetRate = RATE_NOT_SELECTED);

    static bool UseDuplex();

    explicit ProjectAudioManager(AudacityProject& project);
    ProjectAudioManager(const ProjectAudioManager&) = delete;
    ProjectAudioManager& operator=(const ProjectAudioManager&) = delete;
    ~ProjectAudioManager() override;

    bool IsTimerRecordCancelled() { return mTimerRecordCanceled; }
    void SetTimerRecordCancelled() { mTimerRecordCanceled = true; }
    void ResetTimerRecordCancelled() { mTimerRecordCanceled = false; }

    bool Paused() const;

    bool Playing() const;

    // Whether recording into this project (not just into some project) is
    // active
    bool Recording() const;

    bool Stopping() const { return mStopping; }

    // Whether the last attempt to start recording requested appending to tracks
    bool Appending() const { return mAppending; }
    // Whether potentially looping play (using new default PlaybackPolicy)
    bool Looping() const { return mLooping; }
    bool Cutting() const { return mCutting; }

    // A project is only allowed to stop an audio stream that it owns.
    bool CanStopAudioStream() const;

    void OnRecord(bool altAppearance);

    bool DoRecord(AudacityProject& project,
                  //! If captureSequences is empty, then tracks are created
                  const TransportSequences& transportSequences, double t0, double t1, bool altAppearance,
                  const AudioIOStartStreamOptions& options);

    int PlayPlayRegion(const SelectedRegion& selectedRegion, const AudioIOStartStreamOptions& options, PlayMode playMode,
                       bool backwards = false);

    // Play currently selected region, or if nothing selected,
    // play from current cursor.
    void PlayCurrentRegion(
        bool newDefault = false, //!< See ProjectAudioIO::GetDefaultOptions
        bool cutpreview = false);

    void OnPause();

    // Stop playing or recording
    void Stop(bool stopStream = true);

    void StopIfPaused();

    bool DoPlayStopSelect(bool click, bool shift);
    void DoPlayStopSelect();

    PlayMode GetLastPlayMode() const { return mLastPlayMode; }

private:

    void TogglePaused();
    void SetPausedOff();

    void SetAppending(bool value) { mAppending = value; }
    void SetLooping(bool value) { mLooping = value; }
    void SetCutting(bool value) { mCutting = value; }
    void SetStopping(bool value) { mStopping = value; }

    // Cancel the addition of temporary recording tracks into the project
    void CancelRecording();

    // Audio IO callback methods
    void OnAudioIORate(int rate) override;
    void OnAudioIOStartRecording() override;
    void OnAudioIOStopRecording() override;
    void OnAudioIONewBlocks() override;
    void OnCommitRecording() override;
    void OnSoundActivationThreshold() override;

    void OnCheckpointFailure(ProjectFileIOMessage);

    Observer::Subscription mCheckpointFailureSubscription;
    AudacityProject& mProject;

    PlayMode mLastPlayMode{ PlayMode::normalPlay };

    //flag for cancellation of timer record.
    bool mTimerRecordCanceled{ false };

    // Using int as the type for this atomic flag, allows us to toggle its value
    // with an atomic operation.
    std::atomic<int> mPaused{ 0 };

    bool mAppending{ false };
    bool mLooping{ false };
    bool mCutting{ false };
    bool mStopping{ false };

    int mDisplayedRate{ 0 };
    static std::pair< TranslatableStrings, unsigned >
    StatusWidthFunction(
        const AudacityProject& project, StatusBarField field);
};

AudioIOStartStreamOptions DefaultSpeedPlayOptions(AudacityProject& project);

struct PropertiesOfSelected
{
    bool allSameRate{ false };
    int rateOfSelected{ RATE_NOT_SELECTED };
    bool anySelected{ false };
};

AUDACITY_DLL_API
PropertiesOfSelected GetPropertiesOfSelected(const AudacityProject& proj);

#include "CommandFlag.h"

extern AUDACITY_DLL_API const ReservedCommandFlag
& CanStopAudioStreamFlag();

#endif
